// OpentTxl-C Version 11 safe C strings
// J.R. Cordy, Jan 2023

// Copyright 2023, James R. Cordy and others

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
// and associated documentation files (the “Software”), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies 
// or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
// AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// TXL safe strings

// Safe C strings for use in OpenTxl
// When compiled with CHECKED set, this is a completely safe C implementation of strings.
// Provided that all strings are declared using the type "string" and only these operations are used, 
// safety is assured. Longstrings are strings with a 1 Mb length limit.

// All strings in OpenTxl are 1-origin and chars are [1 .. length]

// C standard includes
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

// Exception handling, arithmetic
#include "exceptions.h"
#include "trycatch.h"
#include "assert.h"
#include "arith.h"

// Check interface consistency
#include "strings.h"

// Temporaries used in conversion macros
int stringint_answer;
double stringreal_answer;

#ifdef CHECKED
    // String length
    int stringlen (const string src)
    {
        int i;
        for (i = 1; i <= maxStringLength; i++) {
            if (src[i - 1] == '\0') break;
        }
        return (i - 1);
    }

    int lstringlen (const longstring src)
    {
        int i;
        for (i = 1; i <= maxLongStringLength; i++) {
            if (src[i - 1] == '\0') break;
        }
        return (i - 1);
    }

    // String copy
    void stringcpy (string dest, const string src)
    {
        int i;
        for (i = 1; i <= maxStringLength; i++) {
            if (src[i-1] == '\0') break;
            dest[i-1] = src[i-1];
        }
        dest[i-1] = '\0';
    }

    void lstringcpy (longstring dest, const longstring src)
    {
        int i;
        for (i = 1; i <= maxLongStringLength; i++) {
            if (src[i-1] == '\0') break;
            dest[i-1] = src[i-1];
        }
        dest[i-1] = '\0';
    }

    // String catenate - concatenate src onto dest
    void stringcat (string dest, const string src)
    {
        int i;
        int j = 1;
        for (i = stringlen (dest) + 1; i <= maxStringLength; i++) {
            if (src[j-1] == '\0') break;
            dest[i-1] = src[j-1];
            j++;
        }
        dest[i-1] = '\0';
    }

    void lstringcat (longstring dest, const longstring src)
    {
        int i;
        int j = 1;
        for (i = lstringlen (dest) + 1; i <= maxLongStringLength; i++) {
            if (src[j-1] == '\0') break;
            dest[i-1] = src[j-1];
            j++;
        }
        dest[i-1] = '\0';
    }

#endif

    // String repeat - concatenate several copies of a string
    void stringrep (string dest, const string src, const int n)
    {
        int k = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= stringlen (src); j++) {
                dest[k - 1] = src[j - 1];
                k++;
                if (k == maxStringLength) break;
            }
            if (k == maxStringLength) break;
        }
        dest[k - 1] = '\0';
    }

    void lstringrep (longstring dest, const longstring src, const int n)
    {
        int k = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= lstringlen (src); j++) {
                dest[k - 1] = src[j - 1];
                k++;
                if (k == maxLongStringLength) break;
            }
            if (k == maxLongStringLength) break;
        }
        dest[k - 1] = '\0';
    }

    // Substring 
    void substring (string dest, const string src, const int lower, const int upper)
    {
        int srclen = stringlen (src);
        assert (srclen <= maxStringLength);
        int j = 1;
        for (int i = max (lower, 1); i <= min (upper, srclen); i++) {
            dest[j - 1] = src[i - 1];
            j++;
        }
        dest[j - 1] = '\0';
    }

    void lsubstring (longstring dest, const longstring src, const int lower, const int upper)
    {
        int srclen = lstringlen (src);
        assert (srclen <= maxLongStringLength * 2);
        int j = 1;
        for (int i = max (lower, 1); i <= min (upper, srclen); i++) {
            dest[j - 1] = src[i - 1];
            j++;
        }
        dest[j - 1] = '\0';
    }

    // String index - find the position of the first instance of a string in a string
    int stringindex (const string src, const string pat) {
        int srclen = stringlen (src);
        int patlen = stringlen (pat);
        assert ((srclen <= maxStringLength) && (patlen <= maxStringLength));
        if (pat[0] == '\0') {
            return (1);
        }
        for (int i = 1; i <= srclen; i++) {
            if (src[i - 1] == pat[0]) {
                int s = i;
                for (int p = 1; p <= patlen; p++) {
                    if (src[s - 1] != pat[p - 1]) break;
                    if (p == patlen) {
                        return (i);
                    }
                    s++;
                    if (s > srclen) break;
                }
            }
        }
        return (0);
    }

    int lstringindex (const longstring src, const longstring pat) {
        int srclen = lstringlen (src);
        int patlen = lstringlen (pat);
        assert ((srclen <= maxLongStringLength * 2) && (patlen <= maxLongStringLength));
        if (pat[0] == '\0') {
            return (1);
        }
        for (int i = 1; i <= srclen; i++) {
            if (src[i - 1] == pat[0]) {
                int s = i;
                for (int p = 1; p <= patlen; p++) {
                    if (src[s - 1] != pat[p - 1]) break;
                    if (p == patlen) {
                        return (i);
                    }
                    s++;
                    if (s > srclen) break;
                }
            }
        }
        return (0);
    }

    // Integer to formatted string operations for bases 10 and 16
    void intstring (const int value, const int width, const int base, string target)
    {
        assert ((value >= 0) && ((base == 10) || (base == 16)));
        #define maxDigits 10  // in a positive 32-bit integer in these bases
        char buffer[maxDigits];

        int b = maxDigits;
        int val = value;
        int ndigits = 1;

        while (true) {
            b--;
            assert (b >= 0);
            buffer[b] = val % base;
            if (buffer[b] < 10) {
                buffer[b] += '0';
            } else {
                buffer[b] += 'A' - 10;
            }
            val /= base;
            if (val == 0) break;
            ndigits++;
        }

        int t = 0;
        for (int i = ndigits; i > 0; i--) {
            target[t] = buffer[b];
            b++; t++;
        }
        target[t] = '\0';
    }

    // Upper-to-lower and lower-to-upper case string maps 
    void stringtolower (string srcdest) 
    {
        assert (stringlen (srcdest) <= maxStringLength);
        for (int i = 1; i <= stringlen (srcdest); i++) {
            srcdest[i - 1] = tolower (srcdest[i - 1]);
        }
    }

    void stringtoupper (string srcdest)
    {
        assert (stringlen (srcdest) <= maxStringLength);
        for (int i = 1; i <= stringlen (srcdest); i++) {
            srcdest[i - 1] = toupper (srcdest[i - 1]);
        }
    }

    void lstringtolower (longstring srcdest) 
    {
        assert (lstringlen (srcdest) <= maxLongStringLength);
        for (int i = 1; i <= lstringlen (srcdest); i++) {
            srcdest[i - 1] = tolower (srcdest[i - 1]);
        }
    }

    void lstringtoupper (longstring srcdest)
    {
        assert (lstringlen (srcdest) <= maxLongStringLength);
        for (int i = 1; i <= lstringlen (srcdest); i++) {
            srcdest[i - 1] = toupper (srcdest[i - 1]);
        }
    }

